﻿using nhCore.db;
using nhCore.Modbus;
using nhCore.nMessage;
using nhCore.Server;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Decoder = nhCore.Modbus.Decoder;

namespace nhCore
{
    /// <summary>
    /// 不同接口都在这处理连接、断开、放回、发送
    /// </summary>
    public class NhServer : IIOListener, ISvrListener
    {
        #region "属性"    

        public bool IgnoreFailedSyncMode { get; set; }

        /// <summary>
        /// IO模式，com|tcpServer|usb
        /// </summary>
        public List<IO> Ios { get; set; }

        /// <summary>
        /// 服务是否在运行
        /// </summary>
        private bool IsRuning { get; set; }

        /// <summary>
        /// 连接集合
        /// </summary>
        private List<Conn> Conns { get; set; } = [];

        /// <summary>
        /// 命令解析辅助对象集。要素字典，设备地址为键
        /// </summary>
        //public Dictionary<byte, CmdAnalysisHelp> CmdAnalysisHelps { get; set; } = new Dictionary<byte, CmdAnalysisHelp>();

        /// <summary>
        /// 服务监听器，当前对象转换为ISvrListener类型的对象的属性，可以直接通过该属性来调用接口方法
        /// </summary>
        private ISvrListener SvrListener { get; set; }

        private IAnalysisToDisplay AnalysisToDisplay { get; set; }
        #endregion

        #region "实现IIOListener接口"
        /// <summary>
        /// 异步模式下,设备有数据回来
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="client"></param>
        /// <param name="byt"></param>
        void IIOListener.Received(IO sender, object client, byte[] byt)
        {

        }
        void IIOListener.DisConnected(IO sender, object client)
        {
            //    Conn conn;
            //    lock (Conns)
            //    {
            //        conn = Conns.Find(x => x.Client == client);
            //    }
            //    if (conn == null)
            //    {
            //        return;
            //    }
            //    conn.IsRunning = false; //关task
            //    lock (Conns)
            //    {
            //        Conns.Remove(conn);
            //    }

            //    if (client is System.IO.Ports.SerialPort sp)//客户端是串口则已关闭
            //    {
            //        string str = $"{sp.PortName}接口已关闭。";
            //        lock (Conns)
            //        {
            //            if (Conns.Find(x => x.Client is System.IO.Ports.SerialPort) == null)
            //            {
            //                str += "所有COM接口均已关闭。";
            //            }
            //        }
            //        MsgListener.Message(str);
            //    }
            //    else if (client is System.Net.Sockets.TcpClient tc)//客户端是串口则已关闭
            //    {
            //        string str = $"客户端[{conn.ClientAddress}]已离线。";//显示IP、设备地址
            //        MsgListener.Message(str);
            //    }
        }

        /// <summary>
        /// 有客户端连接上来，每一个连接对应一个Conn对象，每个对象有自己的发送接受线程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="client"></param>
        void IIOListener.Connected(IO sender, object client)
        {
            string handshakeFrame = string.Empty;
            if (client is TcpClient tc)
            {   //接收握手帧
                try
                {
                    NetworkStream stream = tc.GetStream();
                    byte[] bytes = new byte[tc.ReceiveBufferSize];
                    int bytesRead = stream.Read(bytes, 0, tc.ReceiveBufferSize);
                    handshakeFrame = Encoding.UTF8.GetString(bytes, 0, bytesRead);
                }
                catch (Exception)
                {
                    handshakeFrame = DicEnvStat.Singleton.EnvStats.Last().Value.HandshakeFrame;
                    NMessage.Warn($"无握手帧，用首个设备的握手帧代替");
                }
            }

            Station? siteDevice = DicEnvStat.Singleton.EnvStats.Values.FirstOrDefault(x => x.HandshakeFrame == handshakeFrame);
            if (siteDevice == null)
            {
                NMessage.Error($"握手帧【{handshakeFrame}】未找到对应设备。");
                return;
            }

            List<Cmd> ls = [];
            foreach (CmdFrame sc in Cgq组帧(siteDevice))
            {
                if (sc.Automatic)//自动发送命令加到临时命令列表
                {
                    Cmd cmd = new(sc); //命令帮助初始话时DelayTime被赋值
                    ls.Add(cmd);
                }
            }

            Conn conn = new(sender, client, AnalysisResult, handshakeFrame);

            lock (Conns)
            {
                Conns.Add(conn);
            }
            conn.Cmds = ls;

        }

        private static List<CmdFrame> Cgq组帧(Station station)
        {
            List<CmdFrame> ls = [];
            List<ElementConfig> config = station.ElementConfigs.Where(x => x.StationId == station.Id).ToList();//排除映射，映射不组帧。
            var groups = config.GroupBy(item => new { item.DeviceAddress, item.FunctionCode })
                  .Select(group => group.ToList())
                  .ToList();

            foreach (List<ElementConfig> configs in groups)
            {
                List<Decoder> listDecoder = [];
                UInt16 maxReg = (ushort)configs.Max(c => c.RegAddress);

                maxReg += (UInt16)(configs.Single(c => c.RegAddress == maxReg).NDataType.RegCount - 1);
                UInt16 minReg = (UInt16)configs.Min(c => c.RegAddress);
                foreach (var c in configs)
                {
                    Decoder decoder;
                    if (c.DeviceAddress == 0)
                    {
                        //if (!CoreGlobal.计算要素.ContainsKey(dbSensor.Address))
                        //{   //没有握手帧，有多个站点时，才可能出现已有计算字段再次加入
                        //    CoreGlobal.计算要素.Add(
                        //        dbSensor.Address,
                        //        new Dictionary<uint, double?>()
                        //        {
                        //            { (ushort)c.Index, null },
                        //        });
                        //}
                        //decoder = new nhCore.Modbus.NullDecoder(dbSensor.Address, (ushort)c.Index);
                        continue;
                    }
                    else
                    {
                        decoder = NDataType.GetDecoder(c);
                        decoder.ReturnDataIndex = (UInt16)(c.RegAddress - minReg);
                    }
                    decoder.ElementConfig = c;
                    listDecoder.Add(decoder); //加气象解码器到列表
                }

                listDecoder.Add(new CgqTimeDecoder()); //时间也是保存到映射ID

                //实时命令
                RealTimeCmd rtc = new(configs[0].DeviceAddress, configs[0].FunctionCode)
                {
                    Index = minReg,
                    Len = (byte)(maxReg - minReg + 1)
                };
                (rtc.Analyzer as WeatherAnalyzer)!.Decoders = listDecoder;
                ls.Add(rtc);
            }
            return ls;
        }

        void IIOListener.Error(string text)
        {
            NMessage.Error(text);
        }
        #endregion

        #region 服务相关
        /// <summary>
        /// 开启服务
        /// </summary>
        /// <returns>是否正常开启</returns>
        public bool Start()
        {
            if (IsRuning)
            {
                Debug.WriteLine("开启服务时发现服务已经在运行..."); //输出调试信息
                return false;
            }

            if (Ios.Count == 0)
            {
                Debug.WriteLine("未开启IO口，请进入设置窗口设置。"); //输出调试信息
                return false;
            }

            string ioName = "";
            foreach (var io in Ios)
            {
                if (io.Open())//根据IO口类型打开对应的端口
                {
                    ioName += ioName == "" ? io.Name : $",{io.Name}";
                }
            }
            NMessage.Message($"{ioName}服务开启");

            return true;
        }

        /// <summary>
        /// 停止服务，关闭端口
        /// </summary>
        public void Stop()
        {
            IsRuning = false;
            //CmdAnalysisHelps.Clear();
            if (Ios == null) return;
            foreach (var io in Ios)
            {
                io.Close();
            }
        }

        /// <summary>
        /// 设置IO监听器
        /// </summary>
        /// <param name="io"></param>
        public void SetIO(List<IO> ios)
        {
            Ios = ios;
            foreach (IO io in Ios)
            {
                io.SetIOListener(this);
            }
        }

        /// <summary>
        /// 设置服务监听
        /// </summary>
        /// <param name="Listener"></param>
        public void SetListener(IAnalysisToDisplay Listener)
        {
            SvrListener = this;
            AnalysisToDisplay = (IAnalysisToDisplay)Listener;
        }
        #endregion

        /// <summary>
        /// 发送命令，手动，找到在线客户端的自动发送命令，送入Cmds
        /// </summary>
        /// <param name="client">目标客户端，为null时向所有客户端发送</param>
        /// <param name="sentCmd">发送命令</param>
        /// <returns>是否找到对应客户端，命令进入命令列表</returns>
        public bool SendCmd(Modbus.CmdFrame sentCmd, string? handshakeFrame = null)
        {
            bool result = false;
            lock (Conns)
            {
                if (handshakeFrame == null)//未指定客户端，向所有设备发送
                {   //配置命令
                    result = true;
                    foreach (Conn c in Conns)//遍历在线客户端列表,对应的命令帮助
                    {
                        Cmd cmd = new Cmd(sentCmd)
                        {
                            //TimeOut = cmdHelper.TimeOut,//对新实例设置
                        };
                        c.Cmds.Add(cmd);//加入命令列表
                    }
                }
                else //向指定客户端发送
                {
                    Conns.SingleOrDefault(x => x.HandshakeFrame == handshakeFrame)
                        ?.Cmds.Add(
                            new Cmd(sentCmd));
                    result = true;
                }
            }
            return result;
        }

        /// <summary>
        /// 返回帧解析后调用
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="client"></param>
        /// <param name="values"></param>
        /// <param name="sent"></param>
        public void AnalysisResult(IO sender, object values, Cmd cmd)
        {
            CmdFrame sent = cmd.SentCmd;
            //string handshakeFrame = sent.Analyzer.Decoders.First().Config.HandshakeFrame;
            //if (DicEnvStat.Singleton.EnvStats.TryGetValue(handshakeFrame, out SiteDevice? value))
            //{//广播地址回出异常
            //    value?.RecordingCommTime();
            //}

            if (sent is CalibrationTimeCmd)
            {
                if ((bool)values)
                {
                    sent.SleepState = true; //时间校准收到回复后进入休眠
                    NMessage.Message($"{cmd.SentCmd.Address}号设备校时成功。");
                }
                else
                {
                    NMessage.Error($"{cmd.SentCmd.Address}号设备校时失败。");
                }
            }
            else if (sent is ConfigCmd)
            {
                //AnalysisToDisplay.AddEnv((Dictionary<string, List<DbConfig>>)values, cmd);
            }
        }

        /// <summary>
        /// 读出数据库setting表，通讯类型、通讯参数设置服务IO
        /// </summary>
        /// <returns>通讯类型</returns>
        public string SoftwareSet()
        {
            List<IO> ios = [];
            IO io;
            string ioName = "";
            foreach (string mode in NSetting.Singleton.ReadValue(NSetting.con_通讯方式)!.Split(','))
            {
                string modeName = "";
                switch (mode)
                {
                    case NSetting.con_通讯方式_com:
                        io = new SPIO(
                            NSetting.Singleton.ReadValue(NSetting.con_通讯方式_com)!.ToUpper(),
                            Convert.ToInt32(NSetting.Singleton.ReadValue(NSetting.con_波特率)),
                            Convert.ToInt32(NSetting.Singleton.ReadValue(NSetting.con_停止位)),
                            Convert.ToInt32(NSetting.Singleton.ReadValue(NSetting.con_com连接超时)));
                        ios.Add(io);
                        modeName = "串口";
                        break;
                    case NSetting.con_通讯方式_tcp_server:
                        io = new TcpServerIO(int.Parse(NSetting.Singleton.ReadValue(NSetting.con_Tcp服务器端口)!));
                        ios.Add(io);
                        modeName = "TCP服务器";
                        break;
                }
                ioName += ioName == "" ? modeName : $"+{modeName}";
            }

            SetIO(ios);
            return $"{ioName}通讯";
        }

        #region 单例模式
        private static readonly NhServer _nhServer = new();

        //显式的静态构造函数用来告诉C#编译器在其内容实例化之前不要标记其类型
        static NhServer() { }
        //声明一个私有的构造方法，让外部无法调用这个类的构造方法
        private NhServer(){ }

        /// <summary>
        /// 返回对象单例
        /// </summary>
        public static NhServer Singleton
        {
            get { return _nhServer; }
        }
        #endregion
    }
}
